#include "Graphics/OpenGL/GLRenderEngine.h"

#include "Graphics/OpenGL/Win32/GLWin32RenderWindow.h"

#include "Graphics/IViewport.h"

namespace tyro
{
	namespace Graphics
	{

		GLRenderEngine::GLRenderEngine( Engine::Engine* engine )
			: IRenderEngine(engine)
		{
			
		}

		GLRenderEngine::~GLRenderEngine()
		{
			delete renderTarget;
		}

		IRenderWindow* GLRenderEngine::Initialise( const char* const title /*= "Tyro Engine"*/ )
		{
			// Create the render window
			IRenderWindow* window = new GLWin32RenderWindow();

			// Initialise the window
			window->Create(title, 800, 600, false);

			// Set the engines render target
			SetRenderTarget(*window);

			// Return the window
			return window;

		}

		void GLRenderEngine::SwapRenderTargetBuffers( void )
		{
			// Exchange the render targets front and back buffers
			renderTarget->swapBuffers();
		}

		
		void GLRenderEngine::UpdateRenderTarget( void )
		{
			renderTarget->Update();
		}

		FLOAT_32 _angle = 0;

		// index array of vertex array for glDrawElements() & glDrawRangeElement()
		GLubyte indices[]  = { 0, 1, 2,   2, 3, 0,      // front
				4, 5, 6,   6, 7, 4,      // right
				8, 9,10,  10,11, 8,      // top
				12,13,14,  14,15,12,      // left
				16,17,18,  18,19,16,      // bottom
				20,21,22,  22,23,20 };    // back


		GLfloat vertices3[] = { 1, 1, 1,   0, 0, 1,   1, 1, 1,              // v0 (front)
			-1, 1, 1,   0, 0, 1,   1, 1, 0,              // v1
			-1,-1, 1,   0, 0, 1,   1, 0, 0,              // v2
			1,-1, 1,   0, 0, 1,   1, 0, 1,              // v3

			1, 1, 1,   1, 0, 0,   1, 1, 1,              // v0 (right)
			1,-1, 1,   1, 0, 0,   1, 0, 1,              // v3
			1,-1,-1,   1, 0, 0,   0, 0, 1,              // v4
			1, 1,-1,   1, 0, 0,   0, 1, 1,              // v5

			1, 1, 1,   0, 1, 0,   1, 1, 1,              // v0 (top)
			1, 1,-1,   0, 1, 0,   0, 1, 1,              // v5
			-1, 1,-1,   0, 1, 0,   0, 1, 0,              // v6
			-1, 1, 1,   0, 1, 0,   1, 1, 0,              // v1

			-1, 1, 1,  -1, 0, 0,   1, 1, 0,              // v1 (left)
			-1, 1,-1,  -1, 0, 0,   0, 1, 0,              // v6
			-1,-1,-1,  -1, 0, 0,   0, 0, 0,              // v7
			-1,-1, 1,  -1, 0, 0,   1, 0, 0,              // v2

			-1,-1,-1,   0,-1, 0,   0, 0, 0,              // v7 (bottom)
			1,-1,-1,   0,-1, 0,   0, 0, 1,              // v4
			1,-1, 1,   0,-1, 0,   1, 0, 1,              // v3
			-1,-1, 1,   0,-1, 0,   1, 0, 0,              // v2

			1,-1,-1,   0, 0,-1,   0, 0, 1,              // v4 (back)
			-1,-1,-1,   0, 0,-1,   0, 0, 0,              // v7
			-1, 1,-1,   0, 0,-1,   0, 1, 0,              // v6
			1, 1,-1,   0, 0,-1,   0, 1, 1 };            // v5

		void GLRenderEngine::DrawCube()
		{
			glTranslatef(0,0,-10);
			glRotatef(_angle/31, 0, 1, 0);
			glRotatef((_angle/31)*0.25f, 1, 0, 0);
			
			glEnableClientState(GL_NORMAL_ARRAY);
			glEnableClientState(GL_COLOR_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);
			glNormalPointer(GL_FLOAT, 9 * sizeof(GLfloat), vertices3 + 3);
			glColorPointer(3, GL_FLOAT, 9 * sizeof(GLfloat), vertices3 + 6);
			glVertexPointer(3, GL_FLOAT, 9 * sizeof(GLfloat), vertices3);

			glPushMatrix();
			//glTranslatef(-2, 1, 0);                // move to bottom-left

			glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);

			glPopMatrix();

			glDisableClientState(GL_VERTEX_ARRAY);  // disable vertex arrays
			glDisableClientState(GL_COLOR_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);

			_angle += 1.2f;
		}

		void GLRenderEngine::SetViewport( IViewport* viewport )
		{
			GLsizei x, y, w, h;

			w = viewport->GetActWidth();
			h = viewport->GetActHeight();
			x = viewport->GetActLeft();
			y = viewport->GetActTop();

			glViewport(x, y, w, h);

			glScissor(x, y, w, h);
		}

		void GLRenderEngine::SetProjectionMatrix(const Math::Matrix4f& matrix)
		{
			glMatrixMode(GL_PROJECTION);
			glLoadMatrixf(matrix);
			glMatrixMode(GL_MODELVIEW);
		}

		void GLRenderEngine::SetModelViewMatrix()
		{
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
		}

		void GLRenderEngine::BeginFrame( void )
		{
			// Enable the scissor test
			glEnable(GL_SCISSOR_TEST);
		}

		void GLRenderEngine::EndFrame( void )
		{
			// Disable the scissor test
			glDisable(GL_SCISSOR_TEST);
		}

		void GLRenderEngine::ClearBuffers( UINT_32 bufferFlags, const Colour& colour /*= Colour::Grey*/, FLOAT_32 depth /*= 1.0f*/, UINT16 stencil /*= 0.0f*/ )
		{
			// Temp variable for buffer flags
			GLbitfield activeFlags = 0;

			// Test for colour buffer flag
			if (bufferFlags & BUFF_TYPE_COLOUR)
			{
				// Set the colour buffer flag for clearing
				activeFlags |= GL_COLOR_BUFFER_BIT;

				// Clear the frame buffers colour
				glClearColor(colour.red, colour.green, colour.blue, colour.alpha);
			}

			// Test for depth buffer flag
			if (bufferFlags & BUFF_TYPE_DEPTH)
			{
				// Set the depth buffer flag for clearing
				activeFlags |= GL_DEPTH_BUFFER_BIT;

				// Clear the depth buffer
				glClearDepth(depth);

				// Depth testing TODO move to pass
				glEnable(GL_DEPTH_TEST);
				glDepthFunc(GL_LEQUAL);
			}


			// Test for stencil buffer flag
			if (bufferFlags & BUFF_TYPE_STENCIL)
			{
				// Set the stencil buffer flag for clearing
				activeFlags |= GL_STENCIL_BUFFER_BIT;


				// Set the index for clearing
				glClearStencil(stencil);
			}

			// Enable the scissor test
			GLboolean scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST);
			if (!scissorTestEnabled)
			{
				glEnable(GL_SCISSOR_TEST);
			}

			// Scissor against the viewport size
			GLint viewport[4] = { 0, 0, 0, 0 };
			GLint scissor[4] = { 0, 0, 0, 0 };
			glGetIntegerv(GL_VIEWPORT, viewport);
			glGetIntegerv(GL_SCISSOR_BOX, scissor);
			bool scissorBoxDifference =
				viewport[0] != scissor[0] || viewport[1] != scissor[1] ||
				viewport[2] != scissor[2] || viewport[3] != scissor[3];
			if (scissorBoxDifference)
			{
				glScissor(viewport[0], viewport[1], viewport[2], viewport[3]);
			}


			// Clear active buffers
			glClear(activeFlags);

			// Restore scissor box
			if (scissorBoxDifference)
			{
				glScissor(scissor[0], scissor[1], scissor[2], scissor[3]);
			}
			// Restore scissor test
			if (!scissorTestEnabled)
			{
				glDisable(GL_SCISSOR_TEST);
			}

		}

		void GLRenderEngine::MakeGLMatrix( GLfloat gl_mat[16], const Math::Matrix4f& matrix )
		{
			size_t x = 0;
			for (size_t i = 0; i < 4; i++)
			{
				for (size_t j = 0; j < 4; j++)
				{
					gl_mat[x] = matrix.m[j][i];
					x++;
				}
			}

		}

		


	}
}
